// Since this must be included before #include<windows.h>
#if defined(WIN32)
#   include <Winsock2.h>
#   include <Ws2tcpip.h>
#endif

#include "iConfig.hpp"

#if GLF_ENABLE_DEBUGGER

#include <glf/debugger/memoryMonitor.h>
#include <glf/debugger/netInterface.h>
#include <glf/debugger/perfCounters.h>
#include <glf/debugger/profiler.h>
#include <glf/core/types.h>
#include <glf/core/timer.h>
#include <glf/core/thread.h>
#include <cctype>

#if GLF_PLATFORM_WP8
#	include <direct.h>
#	define	MSIZE(p)		0
#elif defined(WIN32)
#	include <Winreg.h>
#	include <imagehlp.h>
#	include <tlhelp32.h>
#	include <direct.h>
#	include <process.h>
#	define MSIZE(p)			_msize(p)
#elif defined(__APPLE__)
#	include <malloc/malloc.h>
#	define	MSIZE(p)		malloc_size(p)
#elif defined(OS_ANDROID)
#	include <malloc.h>
#	define	MSIZE(p)		0
#else
#	define	MSIZE(p)		0
#endif

#ifdef GLF_MALLOC_OVERRIDE
#	ifndef WIN32
#		undef GLF_MALLOC_OVERRIDE
#		define GLF_MALLOC_OVERRIDE 0
#	endif
#else
#	define GLF_MALLOC_OVERRIDE 0
#endif

#if GLF_MALLOC_OVERRIDE

namespace
{
	GLF_DECLARE_TLS(int, mMallocLockCount, extern);
	GLF_DEFINE_TLS(int, mMallocLockCount) = { 0 };

	GLF_DECLARE_TLS(int, mReallocLockCount, extern);
	GLF_DEFINE_TLS(int, mReallocLockCount) = { 0 };

	GLF_DECLARE_TLS(int, mFreeLockCount, extern);
	GLF_DEFINE_TLS(int, mFreeLockCount) = { 0 };

	static int sRecordAllocations = 0;
};

#ifdef _DEBUG
extern "C" void * __cdecl _nh_malloc_dbg(size_t nSize, int nhFlag, int nBlockUse, const char* szFileName, int nLine);
extern "C" void * __cdecl _realloc_dbg(void* pUserData, size_t nNewSize, int nBlockUse, const char* szFileName, int nLine);
#else
extern "C" void * __cdecl _malloc_base(size_t size);
extern "C" void __cdecl _free_base(void* pBlock);
extern "C" void * __cdecl _realloc_base(void* pUserData, size_t nNewSize);
#endif

void* malloc(size_t size)
{
#ifdef _DEBUG
	void* ptr = _nh_malloc_dbg(size, 0, _NORMAL_BLOCK, NULL, 0);
#else
	void* ptr = _malloc_base(size);
#endif

	if(mMallocLockCount.get()++ == 0 && sRecordAllocations)
	{
		glf::debugger::MemoryMonitor::RegisterAllocation(ptr, size, "", 0);
	}
	mMallocLockCount.get()--;
	return ptr;
}

void* realloc(void* ptr, size_t size)
{
#ifdef _DEBUG
	void* newPtr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, NULL, 0);
#else
	void* newPtr = _realloc_base(ptr, size);
#endif

	if(mReallocLockCount.get()++ == 0 && sRecordAllocations)
	{
		glf::debugger::MemoryMonitor::UnregisterAllocation(ptr);
		glf::debugger::MemoryMonitor::RegisterAllocation(newPtr, size, "", 0);
	}
	mReallocLockCount.get()--;
	return newPtr;
}

void free(void* ptr)
{
	if(mFreeLockCount.get()++ == 0 && sRecordAllocations)
	{
		glf::debugger::MemoryMonitor::UnregisterAllocation(ptr);
	}
	mFreeLockCount.get()--;

#ifdef _DEBUG
	_free_dbg(ptr, _NORMAL_BLOCK);
#else
	_free_base(ptr);
#endif
}

#endif // GLF_MALLOC_OVERRIDE

namespace glf
{
namespace debugger
{

inline uint imin(uint a, uint b) { return a < b ? a : b; }
inline uint imax(uint a, uint b) { return a > b ? a : b; }

Atomic32 sInternalAllocatedSize = 0;
Atomic32 sInternalAllocations = 0;

void* Alloc(size_t cnt)
{
#if GLF_MALLOC_OVERRIDE
	mMallocLockCount.get()++;
#endif

	void* p = malloc(cnt);
	sInternalAllocatedSize.fetchAndAdd(MSIZE(p));
	++sInternalAllocations;

#if GLF_MALLOC_OVERRIDE
	mMallocLockCount.get()--;
#endif

	return p;
}

void Free(void* ptr)
{
#if GLF_MALLOC_OVERRIDE
	mFreeLockCount.get()++;
#endif

	sInternalAllocatedSize.fetchAndSub(MSIZE(ptr));
	--sInternalAllocations;
	free(ptr);

#if GLF_MALLOC_OVERRIDE
	mFreeLockCount.get()--;
#endif
}

//-------------------
// Memory Monitor
//-------------------


MemoryMonitor::MemoryMonitor() :
	Module("MEMORYMO"),
	RecordCallStack(false),
	BaseTime(0),
	RecordActivity(false),
	FrameAllocationCount(0),
	FrameDeallocationCount(0),
	FrameAllocatedHeap(0),
	FrameDeallocatedHeap(0)
{
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(OS_WP8)
	_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF );

	const char* recordString = getenv("RECORD_MEMORY_CALLSTACK");
	if(recordString != NULL)
	{
		RecordCallStack |= strcmp(recordString, "true") == 0;
	}
#endif

#if GLF_MALLOC_OVERRIDE
	sRecordAllocations = 1;
	RecordCallStack = true;
#endif

	ScopeMutex mutex;
	InitBaseTime(this);
	CreateAllocator("SYSTEM");
}

GLF_DEFINE_SINGLETON_WITH_DEP(MemoryMonitor, Debugger)

MemoryMonitor::~MemoryMonitor()
{
	// TODO: delete Allocators and Contexts in MemoryContainer dtor

    if( glf::debugger::Debugger::GetInstance() )
    {      
	    glf::debugger::Debugger::GetInstance()->UnregisterModule( this );
    }
}

void MemoryMonitor::RegisterAllocationInternal(void* ptr, size_t size, const char* fileName, uint line, uint allocatorID)
{
	ScopeMutex mutex;

	SMemoryAllocator& allocator = *Allocators[allocatorID];
	AllocMap::iterator it = allocator.Allocations.find(ptr);
	if(it == allocator.Allocations.end())
	{
		SContext* context = GetThreadContextStack().Stack.back();
		SStats& stats = context->getStats(allocatorID);
		stats.AllocationCount++;
		stats.UsedMemory += (uint)size;
		stats.PeakMemory = imax(stats.PeakMemory, stats.UsedMemory);

		SStats& sharedStats = SharedContexts[context->SharedContextIndex].getStats(allocatorID);
		sharedStats.AllocationCount++;
		sharedStats.UsedMemory += (uint)size;
		sharedStats.PeakMemory = imax(sharedStats.PeakMemory, sharedStats.UsedMemory);
		
		std::pair<void*, SAlloc> entry(ptr, SAlloc());
		it = allocator.Allocations.insert(allocator.Allocations.begin(), entry);
		it->second.mSContext = context->Index;
		it->second.mSize = (uint)size;
		it->second.mFileName = fileName;
		it->second.mLine = line;
		it->second.mRefCount = 1;
		it->second.mTimeStamp = GetMicroseconds();
#if defined(WIN32) && !defined(GLF_PLATFORM_WP8)
		if(RecordCallStack)
		{
			SymbolInfo symbolInfo;
			unsigned long callstack[256];
			uint depth = CommonModule::GetInstance()->GetCallStack(callstack, 256, &it->second.mCallStackHash);

			CallStackMap::iterator itCallStack = mAllocationCallStackMap.find(it->second.mCallStackHash);
			if(itCallStack == mAllocationCallStackMap.end())
			{
				CallStackContext* topContext = &mRootCallStackContext;

				for(int i = depth - 1; i >= 0; i--)
				//for(int i = 0; i < depth; i++) // inverse call tree
				{
					unsigned long address = callstack[i];
					AddressMap::const_iterator itAddress = mAddressMap.find(address);
					if(itAddress == mAddressMap.end())
					{
						CommonModule::GetInstance()->ResolveFunctionSymbol(address, symbolInfo);
						std::pair<unsigned long, const char*> entry(address, GetString(symbolInfo.FuncName));
						itAddress = mAddressMap.insert(mAddressMap.begin(), entry);
					}

					CallStackContext::ChildContextMap::iterator itChildContextMap = topContext->mChildContextMap.find(itAddress->second);
					if(itChildContextMap == topContext->mChildContextMap.end())
					{
						StringIndexMap::const_iterator itStringIndex = mSharedCallStackStringIndexMap.find(itAddress->second);
						if(itStringIndex == mSharedCallStackStringIndexMap.end())
						{
							std::pair<const char*, int> entry(itAddress->second, mSharedCallStackContextVector.size());
							itStringIndex = mSharedCallStackStringIndexMap.insert(mSharedCallStackStringIndexMap.begin(), entry);
							
							SSharedContext sharedContext(itAddress->second);
							mSharedCallStackContextVector.push_back(sharedContext);
						}

						mCallStackContextVector.push_back(new CallStackContext(address, mCallStackContextVector.size(), itStringIndex->second, topContext));
						std::pair<const char*, CallStackContext*> entry(itAddress->second, mCallStackContextVector.back());
						itChildContextMap = topContext->mChildContextMap.insert(topContext->mChildContextMap.begin(), entry);
					}

					topContext = itChildContextMap->second;
				}

				std::pair<uint, CallStackContext*> entry(it->second.mCallStackHash, topContext);
				itCallStack = mAllocationCallStackMap.insert(mAllocationCallStackMap.begin(), entry);
			}

			SStats& callStackStats = itCallStack->second->mStats;
			callStackStats.AllocationCount++;
			callStackStats.UsedMemory += (uint)size;
			callStackStats.PeakMemory = imax(callStackStats.PeakMemory, callStackStats.UsedMemory);
		}
#endif

		if(RecordActivity)
		{
			char buffer[64];
			sprintf(buffer, "%u", (unsigned int)ActivityEvents.size());
			debugger::ScopeEvent mem("[glf] Alloc", buffer);

			ActivityEvents.push_back(SEvent(true, ptr, allocatorID, it->second));
		}

		MemoryMonitor::GetInstance()->FrameAllocationCount++;
		MemoryMonitor::GetInstance()->FrameAllocatedHeap += (uint)size;
	}
	else
	{
		//printf("");
	}
}

void MemoryMonitor::UnregisterAllocationInternal(void* ptr, uint allocatorID)
{
	ScopeMutex mutex;

	SMemoryAllocator& allocator = *Allocators[allocatorID];
	AllocMap::iterator it = allocator.Allocations.find(ptr);
	if(it != allocator.Allocations.end())
	{
		//GLF_ASSERT(it->second.mRefCount >= 1);

		SStats& stats = Contexts[it->second.mSContext]->getStats(allocatorID);
		stats.AllocationCount--;
		stats.UsedMemory -= it->second.mSize;

		SStats& sharedStats = SharedContexts[Contexts[it->second.mSContext]->SharedContextIndex].getStats(allocatorID);
		sharedStats.AllocationCount--;
		sharedStats.UsedMemory -= it->second.mSize;

#if defined(WIN32) && !defined(GLF_PLATFORM_WP8)
		CallStackMap::iterator itCallStack = mAllocationCallStackMap.find(it->second.mCallStackHash);
		if(itCallStack != mAllocationCallStackMap.end())
		{
			SStats& callStackStats = itCallStack->second->mStats;
			callStackStats.AllocationCount--;
			callStackStats.UsedMemory -= it->second.mSize;
		}
#endif

		if(RecordActivity)
		{
			char buffer[64];
			sprintf(buffer, "%u", (unsigned int)ActivityEvents.size());
			debugger::ScopeEvent mem("[glf] Dealloc", buffer);

			SAlloc alloc = it->second;
			alloc.mTimeStamp = GetMicroseconds();
			ActivityEvents.push_back(SEvent(false, ptr, allocatorID, alloc));
		}

		MemoryMonitor::GetInstance()->FrameDeallocationCount++;
		MemoryMonitor::GetInstance()->FrameDeallocatedHeap += it->second.mSize;

		allocator.Allocations.erase(it);
	}
	else
	{
		//printf("");
		//GLF_ASSERT(false);
	}
}

uint MemoryMonitor::GetTotalAllocatedInternal(uint allocatorID)
{
	ScopeMutex mutex;

	uint total = 0;
	for(uint i = 0; i < Contexts.size(); i++)
	{
		SStats& stats = Contexts[i]->getStats(allocatorID);
		total += stats.UsedMemory;
	}
	return total;
}

MemoryMonitor::SContext* MemoryMonitor::CreateContext(const char* name)
{
	uint sharedContext = 0;
	for(; sharedContext < SharedContexts.size(); sharedContext++)
	{
		if(SharedContexts[sharedContext].Name == name)
			break;
	}
	if(sharedContext >= SharedContexts.size())
	{
		sharedContext = SharedContexts.size();
		SharedContexts.push_back(SSharedContext(name));
	}

	Contexts.push_back(new SContext(Contexts.size(), sharedContext));
	return Contexts.back();
}

void MemoryMonitor::PushContext(const char* name)
{
	ScopeMutex mutex;

	SThreadContextStack& stack = GetThreadContextStack();
	SContext::ChildContextMap::iterator it = stack.Stack.back()->ChildContexts.find(name);
	if(it == stack.Stack.back()->ChildContexts.end())
	{
		SContext* context = CreateContext(name);
		stack.Stack.back()->ChildContexts[name] = context;
		stack.Stack.push_back(context);
	}
	else
	{
		stack.Stack.push_back(it->second);
	}
}

void MemoryMonitor::PopContext()
{
	ScopeMutex mutex;

	GetThreadContextStack().Stack.pop_back();
}

uint MemoryMonitor::CreateAllocator(const char* name)
{
	uint allocatorID = Allocators.size();
	Allocators.push_back(new SMemoryAllocator(name));
	return allocatorID;
}

MemoryMonitor::SThreadContextStack& MemoryMonitor::GetThreadContextStack()
{
	uint threadID = 0;

#ifdef WIN32
	threadID = (uint)GetCurrentThreadId();
#endif

	SThreadContextStackMap::iterator it = ThreadContextStacks.find(threadID);
	if(it != ThreadContextStacks.end())
		return it->second;

	stringdbg defaultName = "<default>";

#if defined(WIN32) && !defined(GLF_PLATFORM_WP8)
	unsigned long callStack[32] = {0};
	uint depth = CommonModule::GetInstance()->GetCallStack(callStack, 32);
	if(ThreadContextStacks.size() > 0 && depth >= 1)
	{
		SymbolInfo symbolInfo;
		CommonModule::GetInstance()->ResolveFunctionSymbol(callStack[depth-1], symbolInfo);
		defaultName = "<";
		defaultName += symbolInfo.FuncName;
		defaultName += ">";
	}
#endif

	std::pair<uint, SThreadContextStack> entry(threadID, SThreadContextStack());
	it = ThreadContextStacks.insert(ThreadContextStacks.begin(), entry);
	it->second.Stack.push_back(CreateContext(defaultName.c_str()));
	return it->second;
}

void MemoryMonitor::ResetPerformanceCounters()
{
	FrameAllocationCount = 0;
	FrameDeallocationCount = 0;
	FrameAllocatedHeap = 0;
	FrameDeallocatedHeap = 0;
}

void MemoryMonitor::InitBaseTime(MemoryMonitor* refThis)
{
	refThis->BaseTime = GetMicroseconds();
}

void MemoryMonitor::InitBaseTime()
{
	InitBaseTime(GetInstance());
}

void MemoryMonitor::SendSnapshot(const char* name)
{
	SendMemoryMonitorSnapshot(this, name, true, true);
}

void MemoryMonitor::UpdateFrame()
{
	ScopeMutex mutex;

	if(RecordActivity)
	{
		SAlloc alloc;
		ActivityEvents.push_back(MemoryMonitor::SEvent(true, NULL, MemoryMonitor::SYSTEM_ALLOCATOR, alloc));
	}

	//	// update memory monitor counters
	PerfCounters::UpdateValue("AllocCount.SAlloc", FrameAllocationCount);
	PerfCounters::UpdateValue("AllocCount.Dealloc", FrameDeallocationCount);
	PerfCounters::UpdateValue("AllocCount.Net", FrameAllocationCount - FrameDeallocationCount);
	PerfCounters::UpdateValue("AllocSize.SAlloc", FrameAllocatedHeap);
	PerfCounters::UpdateValue("AllocSize.Dealloc", FrameDeallocatedHeap);
	PerfCounters::UpdateValue("AllocSize.Net", FrameAllocatedHeap - FrameDeallocatedHeap);

	char buffer[256];
	for(unsigned int allocatorID = 0; allocatorID < Allocators.size(); allocatorID++)
	{
		sprintf(buffer, "Alloc.%s", Allocators[allocatorID]->Name.c_str());
		PerfCounters::UpdateValue(buffer, GetTotalAllocatedInternal(allocatorID));
	}
	
	ResetPerformanceCounters();
}

void MemoryMonitor::Parse(int type, PacketReader& in)
{
	switch(type)
	{
		case MESSAGE_REQUEST_REFRESH:
		{
			SendMemoryMonitorRefresh();
			break;
		}

		case MESSAGE_REQUEST_SNAPSHOT:
		{
			bool allocations = in.ReadIntLE() != 0;
			bool callStacks = in.ReadIntLE() != 0;
			bool leaks = in.ReadIntLE() != 0;

			if(leaks)
			{
#ifdef WIN32
				SendMemoryLeaks("Leaks");
#endif
			}
			else
			{
				SendMemoryMonitorSnapshot(this, "Unnamed", allocations, callStacks);
			}
			break;
		}

		case MESSAGE_SET_OPTIONS:
		{
			int crtDbgFreq = in.ReadIntLE();
			Profiler::GetInstance()->SetCrtDbgFrequency(crtDbgFreq);
			break;
		}

		case MESSAGE_START_ACTIVITY:
		{
			MemoryMonitor::GetInstance()->RecordActivity = true;
			break;
		}

		case MESSAGE_STOP_ACTIVITY:
		{
			MemoryMonitor::GetInstance()->RecordActivity = false;
			SendMemoryMonitorRefresh();
			SendMemoryMonitorActivity();
			break;
		}

		case MESSAGE_REQUEST_CALLSTACK:
		{
			unsigned long hash = (unsigned long)in.ReadIntLE();
			SendMemoryCallStack(hash);
			break;
		}

		default:
			break;
	};
}

void MemoryMonitor::DebuggerDestroyed()
{
	//CreateSnapshot("Leaks");
}

void MemoryMonitor::SendMemoryMonitorRefresh()
{
	ScopeMutex mutex;
	MemoryMonitor* monitor = MemoryMonitor::GetInstance();

	PacketWriter& pw = GetPacketWriter();
	pw.Init(MESSAGE_REFRESH);

	pw.Write((uint)monitor->Allocators.size());
	for(uint allocatorID = 0; allocatorID < monitor->Allocators.size(); allocatorID++)
	{
		MemoryMonitor::SMemoryAllocator& allocator = *monitor->Allocators[allocatorID];
		pw.Write(allocator.Name.c_str());
		pw.Write(allocator.Capacity);

		// shared contexts
		pw.Write((uint)monitor->SharedContexts.size());
		for(uint i = 0; i < monitor->SharedContexts.size(); i++)
		{
			MemoryMonitor::SSharedContext& context = monitor->SharedContexts[i];
			MemoryMonitor::SStats& stats = context.getStats(allocatorID);
			pw.Write(context.Name.c_str());
			pw.Write(stats.AllocationCount);
			pw.Write(stats.UsedMemory);
			pw.Write(stats.PeakMemory);
		}

		// contexts
		pw.Write((uint)monitor->Contexts.size());
		for(uint i = 0; i < monitor->Contexts.size(); i++)
		{
			MemoryMonitor::SContext* context = monitor->Contexts[i];
			MemoryMonitor::SStats& stats = context->getStats(allocatorID);
			pw.Write(context->SharedContextIndex);
			pw.Write(stats.AllocationCount);
			pw.Write(stats.UsedMemory);
			pw.Write(stats.PeakMemory);
			pw.Write((uint)context->ChildContexts.size());
			for(MemoryMonitor::SContext::ChildContextMap::const_iterator it = context->ChildContexts.begin(); it != context->ChildContexts.end(); ++it)
				pw.Write(it->second->Index);
		}
	}

	Send(pw);
}

typedef std::map<const char*, uint, std::less<const char*>, DebuggerAllocator<std::pair<const char* const, uint> > > FileNameMap;

void MemoryMonitor::SendMemoryMonitorSnapshot(MemoryContainer* mc, const char* name, bool allocations, bool callStacks)
{
	ScopeMutex mutex;
	MemoryContainer* monitor = mc;
	PacketWriter& pw = GetPacketWriter();

	//if(allocations)
	//{
	//	uint bufferSize = 0;
	//	for(uint allocatorID = 0; allocatorID < monitor->Allocators.size(); allocatorID++)
	//	{
	//		MemoryMonitor::SMemoryAllocator& allocator = *monitor->Allocators[allocatorID];
	//		bufferSize += (uint)allocator.Allocations.size() * sizeof(MemoryMonitor::SAlloc);
	//	}
	//	pw.reserve(bufferSize);
	//}

	pw.Init(MESSAGE_SNAPSHOT);

	pw.Write(name);
	pw.Write((uint)monitor->Allocators.size() + 1);
	for(uint allocatorID = 0; allocatorID < monitor->Allocators.size(); allocatorID++)
	{
		MemoryMonitor::SMemoryAllocator& allocator = *monitor->Allocators[allocatorID];
		pw.Write(allocator.Name.c_str());
		pw.Write(allocator.Capacity);

		// shared contexts
		pw.Write((uint)monitor->SharedContexts.size());
		for(uint i = 0; i < monitor->SharedContexts.size(); i++)
		{
			MemoryMonitor::SSharedContext& context = monitor->SharedContexts[i];
			MemoryMonitor::SStats& stats = context.getStats(allocatorID);
			pw.Write(context.Name.c_str());
			pw.Write(stats.AllocationCount);
			pw.Write(stats.UsedMemory);
			pw.Write(stats.PeakMemory);
		}

		// contexts
		pw.Write((uint)monitor->Contexts.size());
		for(uint i = 0; i < monitor->Contexts.size(); i++)
		{
			MemoryMonitor::SContext* context = monitor->Contexts[i];
			MemoryMonitor::SStats& stats = context->getStats(allocatorID);
			pw.Write(context->SharedContextIndex);
			pw.Write(stats.AllocationCount);
			pw.Write(stats.UsedMemory);
			pw.Write(stats.PeakMemory);
			pw.Write((uint)context->ChildContexts.size());
			for(MemoryMonitor::SContext::ChildContextMap::const_iterator it = context->ChildContexts.begin(); it != context->ChildContexts.end(); ++it)
				pw.Write(it->second->Index);
		}

		// detailed allocations
		pw.Write(allocations);
		if(allocations)
		{
			uint64 baseTime = MemoryMonitor::GetInstance()->BaseTime;

			std::vector<const char*, DebuggerAllocator<const char*> > fileNames;
			FileNameMap fileNameMap;
			
			pw.Write((uint)allocator.Allocations.size());
			for(MemoryMonitor::AllocMap::const_iterator it = allocator.Allocations.begin(); it != allocator.Allocations.end(); ++it)
			{
				pw.Write((uint)it->first);
				pw.Write(it->second.mSContext);
				pw.Write(it->second.mSize);

				FileNameMap::const_iterator itName = fileNameMap.find(it->second.mFileName);
				if(itName == fileNameMap.end())
				{
					std::pair<const char* const, uint> value(it->second.mFileName, fileNames.size());
					itName = fileNameMap.insert(fileNameMap.end(), value);
					fileNames.push_back(it->second.mFileName);
				}

				pw.Write(itName->second);
				pw.Write(it->second.mLine);
				pw.Write(it->second.mRefCount);
				pw.Write((int64)it->second.mTimeStamp < (int64)baseTime ? 0 : it->second.mTimeStamp - baseTime);
				pw.Write(it->second.mCallStackHash);
			}

			// file names
			pw.Write((uint)fileNames.size());
			for(uint i = 0; i < fileNames.size(); i++)
				pw.Write(fileNames[i] != NULL ? fileNames[i] : "");
		}
	}

	{
		// CallStack
		pw.Write("CallStack");
		pw.Write(Allocators[0]->Capacity);

		MemoryMonitor::SStats stats;
		pw.Write((uint)mSharedCallStackContextVector.size());
		for(uint i = 0; i < mSharedCallStackContextVector.size(); i++)
		{
			SSharedContext& context = mSharedCallStackContextVector[i];
			pw.Write(context.Name.c_str());
			pw.Write(stats.AllocationCount);
			pw.Write(stats.UsedMemory);
			pw.Write(stats.PeakMemory);
		}

		// contexts
		pw.Write((uint)mCallStackContextVector.size());
		for(uint i = 0; i < mCallStackContextVector.size(); i++)
		{
			CallStackContext* context = mCallStackContextVector[i];
			MemoryMonitor::SStats& stats = context->mStats;
			pw.Write(context->mSharedIndex);
			pw.Write(stats.AllocationCount);
			pw.Write(stats.UsedMemory);
			pw.Write(stats.PeakMemory);
			pw.Write((uint)context->mChildContextMap.size());
			for(CallStackContext::ChildContextMap::const_iterator it = context->mChildContextMap.begin(); it != context->mChildContextMap.end(); ++it)
				pw.Write(it->second->mIndex);
		}

		pw.Write(false);
	}

	// CallStack hashes
	pw.Write((uint)mAllocationCallStackMap.size());
	for(CallStackMap::const_iterator it = mAllocationCallStackMap.begin(); it != mAllocationCallStackMap.end(); ++it)
	{
		pw.Write((uint)it->first);
		pw.Write((uint)it->second->mIndex);
	}

	Send(pw);
}

void MemoryMonitor::SendMemoryLeaks(const char* name)
{
	ScopeMutex mutex;

	MemoryLeakDetector leakDetector(this);
	leakDetector.Scan();
	SendMemoryMonitorSnapshot(&leakDetector, name, true, true);
}

void MemoryMonitor::SendMemoryMonitorActivity()
{
	ScopeMutex mutex;
	MemoryMonitor* monitor = MemoryMonitor::GetInstance();
	PacketWriter& pw = GetPacketWriter();

	pw.Init(MESSAGE_ACTIVITY);

	pw.Write((uint)monitor->ActivityEvents.size());
	for(uint i = 0; i < monitor->ActivityEvents.size(); i++)
	{
		MemoryMonitor::SEvent& event = monitor->ActivityEvents[i];
		pw.Write(event.Allocation);
		pw.Write((uint)event.Address);
		pw.Write(event.AllocatorID);
		pw.Write(event.Alloc.mSContext);
		pw.Write(event.Alloc.mSize);
		pw.Write(event.Alloc.mFileName != NULL ? event.Alloc.mFileName : "");
		pw.Write(event.Alloc.mLine);
		pw.Write(event.Alloc.mTimeStamp);
		pw.Write(event.Alloc.mCallStackHash);
	}

	Send(pw);

	monitor->ActivityEvents.clear();
}

void MemoryMonitor::SendMemoryCallStack(unsigned long hash)
{
	ScopeMutex mutex;

	CallStackMap::const_iterator it = mAllocationCallStackMap.find(hash);
	if(it != mAllocationCallStackMap.end())
	{
		CallStackContext* context = it->second;
		std::vector<unsigned long, DebuggerAllocator<unsigned long> > addresses;
		while(context != NULL)
		{
			addresses.insert(addresses.begin(), context->mAddress);
			context = context->mParent;
		}
		CommonModule::GetInstance()->SendCallStackSymbols(GetName(), "", addresses.size(), &addresses[0]);
	}
}

//---------------------
// Memory Leak Detector
//---------------------

#define ADDRESS_SHIFT_SIZE		2
#define ADDRESS_TO_INDEX(_a)	(((uint)_a - (uint)mHeapRange.mBegin) >> ADDRESS_SHIFT_SIZE)
#define MAX_HERITANCE			16

MemoryLeakDetector::MemoryLeakDetector(MemoryMonitor* monitor) :
	mMonitor(monitor)
{

}

void MemoryLeakDetector::Scan()
{
	ScopeMutex mutex;

	glf::Console::Println("MemoryLeakDetector::Scan");

	CopyContexts();
	InitRanges();

	uint64 begin = GetMicroseconds();
	SMemoryAllocator& allocator = *mMonitor->Allocators[MemoryMonitor::SYSTEM_ALLOCATOR];
	SMemoryAllocator& leaksAllocator = *Allocators[MemoryMonitor::SYSTEM_ALLOCATOR];

	// Scan statics
	ScanRange(allocator, mStaticRange.mBegin, mStaticRange.mEnd);

	// Scan main thread
	//ScanRange(allocator, mStaticRange.mBegin, mStaticRange.mEnd);

	uint64 end = GetMicroseconds();
	glf::Console::Println("Time=%d ms", (end-begin)/1000);

	// Check unreferenced blocks and register leaks
	for(MemoryMonitor::AllocMap::iterator it = allocator.Allocations.begin(); it != allocator.Allocations.end(); ++it)
	{
		if(it->first != NULL)
		{
			const char& slot = mAllocVector[ADDRESS_TO_INDEX(it->first)];
			if(it->second.mRefCount == 0)
			{
				// register leaked allocation
				const int allocatorID = 0;
				SContext* context = Contexts[it->second.mSContext];
				SStats& stats = context->getStats(allocatorID);
				stats.AllocationCount++;
				stats.UsedMemory += (uint)it->second.mSize;
				stats.PeakMemory = imax(stats.PeakMemory, stats.UsedMemory);

				SStats& sharedStats = SharedContexts[context->SharedContextIndex].getStats(allocatorID);
				sharedStats.AllocationCount++;
				sharedStats.UsedMemory += (uint)it->second.mSize;
				sharedStats.PeakMemory = imax(sharedStats.PeakMemory, sharedStats.UsedMemory);

				std::pair<void*, SAlloc> entry(it->first, it->second);
				leaksAllocator.Allocations.insert(leaksAllocator.Allocations.begin(), entry);
			}
			else
			{
			}
		}
	}

	// scan references inside leaks
	for(MemoryMonitor::AllocMap::iterator it = leaksAllocator.Allocations.begin(); it != leaksAllocator.Allocations.end(); ++it)
	{
		if(it->first != NULL)
		{
			if(it->second.mRefCount == 0)
			{
				ScanRange(leaksAllocator, it->first, (char*)it->first + it->second.mSize);
			}
		}
	}
	glf::Console::Println("Done.");
}

static void AssignChildren(MemoryContainer& mc, MemoryContainer::SContext* dst, MemoryContainer::SContext* src)
{
	for(MemoryContainer::SContext::ChildContextMap::const_iterator it = src->ChildContexts.begin(); it != src->ChildContexts.end(); ++it)
	{
		dst->ChildContexts[mc.SharedContexts[it->second->SharedContextIndex].Name.c_str()] = mc.Contexts[it->second->Index];
		AssignChildren(mc, mc.Contexts[it->second->Index], it->second);
	}
}

void MemoryLeakDetector::CopyContexts()
{
	Allocators.push_back(new SMemoryAllocator("SYSTEM"));
	
	for(size_t i = 0; i < mMonitor->SharedContexts.size(); i++)
	{
		SharedContexts.push_back(SSharedContext(mMonitor->SharedContexts[i].Name.c_str()));
	}

	for(size_t i = 0; i < mMonitor->Contexts.size(); i++)
	{
		Contexts.push_back(new MemoryContainer::SContext(mMonitor->Contexts[i]->Index, mMonitor->Contexts[i]->SharedContextIndex));
	}

	for(size_t i = 0; i < mMonitor->Contexts.size(); i++)
	{
		AssignChildren(*this, Contexts[i], mMonitor->Contexts[i]);
	}
}

// http://www.developpez.net/forums/d864223/c-cpp/outils-c-cpp/visual-cpp/data-segment-data-p-bss-p-heap-linker-variables/
// http://stackoverflow.com/questions/6126980/get-pointer-to-image-dos-header-with-getmodulehandle
// http://msdn.microsoft.com/en-us/library/ms680341%28v=vs.85%29.aspx
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
// http://fr.wikipedia.org/wiki/Portable_Executable_File_Format
void MemoryLeakDetector::InitRanges()
{
	mHeapRange.mBegin = (void*)0xffffffff;
	mHeapRange.mEnd = (void*)0;

	SMemoryAllocator& allocator = *mMonitor->Allocators[MemoryMonitor::SYSTEM_ALLOCATOR];

	// Compute heap range
	for(AllocMap::iterator it = allocator.Allocations.begin(); it != allocator.Allocations.end(); ++it)
	{
		if(it->first != NULL)
		{
			mHeapRange.mBegin = (void*)imin((uint)it->first, (uint)mHeapRange.mBegin);
			mHeapRange.mEnd = (void*)imax((uint)it->first + it->second.mSize, (uint)mHeapRange.mEnd);
		}
		it->second.mRefCount = 0;
	}

	const size_t heapRange = (uint)mHeapRange.mEnd - (uint)mHeapRange.mBegin + (MAX_HERITANCE * sizeof(int));
	mAllocVector.resize(heapRange >> ADDRESS_SHIFT_SIZE);
	memset(&mAllocVector[0], 0xff, mAllocVector.size());

	// Update allocation vector
	for(AllocMap::iterator it = allocator.Allocations.begin(); it != allocator.Allocations.end(); ++it)
	{
		if(it->first != NULL)
		{
			size_t internalOffset = 0;
			for(size_t i = 0; i < MAX_HERITANCE && internalOffset < it->second.mSize; i++)
			{
				mAllocVector[ADDRESS_TO_INDEX(it->first) + i] = i;
				internalOffset += sizeof(int);
			}
			const char& slot = mAllocVector[ADDRESS_TO_INDEX(it->first)];
			GLF_ASSERT(slot == STATE_ALLOCATION_BASE);
		}
	}

#if defined(WIN32) && !defined(GLF_PLATFORM_WP8)
	HMODULE hModule = GetModuleHandle(NULL);
	if(hModule != NULL)
	{
		IMAGE_DOS_HEADER* pDOSHeader = (IMAGE_DOS_HEADER*)hModule;
		IMAGE_NT_HEADERS* pNTHeaders =(IMAGE_NT_HEADERS*)((BYTE*)pDOSHeader + pDOSHeader->e_lfanew);
		IMAGE_FILE_HEADER* pe = &pNTHeaders->FileHeader;
		IMAGE_SECTION_HEADER* se = (IMAGE_SECTION_HEADER*)(((BYTE*)pNTHeaders) + sizeof(IMAGE_NT_HEADERS));

		HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
		if(hModuleSnap == INVALID_HANDLE_VALUE)
			return;
		
		MODULEENTRY32 me32;
		me32.dwSize = sizeof(MODULEENTRY32);
		if(Module32First(hModuleSnap, &me32))
		{
			for(WORD i = 0; i < pe->NumberOfSections; i++)
			{
				if(strcmp((const char*)se->Name, ".data") == 0)
				{
					glf::Console::Println("   Static Data: %d bytes", se->Misc.VirtualSize);
					mStaticRange.mBegin = me32.modBaseAddr + se->VirtualAddress;
					mStaticRange.mEnd = me32.modBaseAddr + se->VirtualAddress + se->Misc.VirtualSize;
				}
				se++;
			}

			CloseHandle(hModuleSnap);
		}
	}

#endif
}

void MemoryLeakDetector::ScanRange(SMemoryAllocator& allocator, void* startAddress, void* stopAddress)
{
	void** p = (void**)startAddress;
	void** end = (void**)stopAddress;
	
	for(; p < end; p++)
	{
		void* ptr = *p;

		// Invalid address?
		if((ptr < mHeapRange.mBegin) || (ptr > mHeapRange.mEnd) || ((int)ptr & 0x03))
			continue;

		// Visited address?
		const char& slot = mAllocVector[ADDRESS_TO_INDEX(ptr)];
		if(slot == STATE_INVALID)
			continue;

		// Find allocation size
		GLF_ASSERT(slot >= STATE_ALLOCATION_BASE);
		void* ptrBase = (void*)((uint)ptr - (slot * sizeof(void**)));
		AllocMap::iterator it = allocator.Allocations.find(ptrBase);
		//GLF_ASSERT(it != allocator.Allocations.end());

		if(it == allocator.Allocations.end())
			continue;

		it->second.mRefCount++;
		
		// skip scan if block is smaller than one pointer
		if((it->second.mSize < sizeof(void**)) || it->second.mRefCount > 1)
			continue;

		ScanRange(allocator, it->first, (char*)it->first + it->second.mSize);
	}
}

} //namespace debugger
} //namespace glf

#endif // GLF_ENABLE_DEBUGGER
